using System;
using Firefly.ExtensionMethods;
using Firefly.Framework.Graphics;
using Microsoft.Xna.Framework;

namespace Firefly.Framework.Components.Cameras
{
    public abstract class CameraBase : UpdateableComponent, ICamera
    {
        public CameraBase(Game game)
            : base(game)
        {
            NearClipPlane = 0.1f;
            FarClipPlane = 20000;
            FieldOfView = 45.0f;
            Position = new Vector3(0.0f, 0.0f, -20f);
            EulerRotation = new Vector3(1.0f, 0.0f, 0.0f);
            TranslationStep = 12.0f;
            RotationClamp = 200.0f;
            RotationDelta = 1.0f;
            ResetOnRollover = true;
        }

        #region ICamera Members

        public bool ResetOnRollover { get; set; }
        public float Yaw { get; set; }
        public float Pitch { get; set; }
        public float Roll { get; set; }
        public float NearClipPlane { get; set; }
        public float FarClipPlane { get; set; }
        public float FieldOfView { get; set; }
        public BoundingFrustum BoundingFrustum
        {
            get { return new BoundingFrustum(View * Projection); }
        }
        public Matrix ViewProjection
        {
            get { return View * Projection; }
        }
        public Matrix WorldViewProjection
        {
            get { return World * View * Projection; }
        }

        #endregion

        #region IUpdateable Members

        public override void Update(GameTime gameTime)
        {
            if (Enabled)
            {
                Rotation = Matrix.CreateFromYawPitchRoll(Yaw, Pitch, Roll);
                Projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(FieldOfView),
                    GraphicsService.Instance.AspectRatio,
                    NearClipPlane,
                    FarClipPlane);
                View = Matrix.CreateLookAt(Position, Position + Rotation.Forward, Vector3.Up);
                World = Matrix.Identity * View * Projection;
            }
        }

        #endregion

        #region IHasViews Members

        public virtual Matrix Projection { get; protected set; }
        public virtual Matrix View { get; protected set; }
        public virtual Matrix World { get; protected set; }

        #endregion

        #region ISpatial3D Members

        public Vector3 Position { get; set; }
        public Vector3 Offset { get; set; }
        public Vector3 EulerRotation
        {
            get { return Rotation.ToVector3(); }
            set { Rotation = ((Vector3)value).ToRotation(); }
        }
        public Matrix Rotation { get; set; }
        public Vector3 Scale { get; set; }
        public BoundingBox BoundingBox
        {
            get { return new BoundingBox(Position - (Scale / 2) + Offset, Position + (Scale / 2) + Offset); }
        }

        public void SetPosition(float x, float y, float z)
        {
            Position = new Vector3(x, y, z);
        }

        public void SetOffset(float x, float y, float z)
        {
            Offset = new Vector3(x, y, z);
        }

        #endregion

        // TODO : finish the methods here.
        #region ITransformable3D Members

        public float TranslationStep { get; set; }
        public float RotationClamp { get; set; }
        public float RotationDelta { get; set; }

        public virtual void MoveForward(float step)
        {
            Position = Position + (Rotation.Forward * step);
        }

        public virtual void MoveBackward(float step)
        {
            Position = Position + (Rotation.Backward * step);
        }

        public virtual void MoveUp(float step)
        {
            Position = Position + (Rotation.Up * step);
        }

        public virtual void MoveDown(float step)
        {
            Position = Position + (Rotation.Down * step);
        }

        public virtual void MoveLeft(float step)
        {
            Position = Position + (Rotation.Left * step);
        }

        public virtual void MoveRight(float step)
        {
            Position = Position + (Rotation.Right * step);
        }

        public virtual void RotateUp(float delta)
        {
        }

        public virtual void RotateDown(float delta)
        {
        }

        public virtual void RotateLeft(float delta)
        {
        }

        public virtual void RotateRight(float delta)
        {
        }

        public virtual void Rotate(Vector3 delta)
        {
            Yaw += -delta.X / RotationClamp;
            Pitch += -delta.Y / RotationClamp;
            Roll += -delta.Z / RotationClamp;

            if (ResetOnRollover)
            {
                if (Pitch > Math.PI / 2f)
                {
                    Pitch = (float)Math.PI / 2f;
                }

                if (Pitch < -Math.PI / 2f)
                {
                    Pitch = -(float)Math.PI / 2f;
                }
            }
        }

        #endregion

        #region IDisposable Members

        protected override void Dispose(bool disposing)
        {
            if (!Disposed)
            {
                if (disposing)
                {
                }

                Disposed = true;
            }
        }

        ~CameraBase()
        {
            Dispose(false);
        }

        #endregion
    }
}
